/*
 * Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef VIRTNET_UTIL_H
#define VIRTNET_UTIL_H

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <syslog.h>

#ifdef __cplusplus
extern "C" {
#endif

#define CONFIG_STR_SIZE		64
#define STATUS_ID_SIZE		6
#define PCI_ADDR_STR_LEN	12

#ifndef MIN
#define MIN(a, b) \
	__extension__ ({ \
		typeof (a) _a = (a); \
		typeof (b) _b = (b); \
		_a < _b ? _a : _b; \
	})
#endif

#ifndef MAX
#define MAX(a, b) \
	__extension__ ({ \
		typeof (a) _a = (a); \
		typeof (b) _b = (b); \
		_a > _b ? _a : _b; \
	})
#endif

#define ALIGN_FLOOR(val, align) \
	(typeof(val))((val) & (~((typeof(val))((align) - 1))))

#define ALIGN_CEIL(val, align) \
	ALIGN_FLOOR(((val) + ((typeof(val)) (align) - 1)), align)

#define	DIM(a) \
	(sizeof (a) / sizeof ((a)[0]))

#define TEST(_cond_) \
	virtnet_test_condition(VIRTNET_TEST_##_cond_)

#define log_error(M, ...) \
        syslog(LOG_ERR, "[ERROR] %s:%d:%s: " M "\n", \
                    __FILE__, __LINE__, __func__, ##__VA_ARGS__)

#define log_info(M, ...) \
        syslog(LOG_INFO,  "[INFO]  %s:%d:%s: " M "\n", \
                    __FILE__, __LINE__, __func__, ##__VA_ARGS__)

#define log_debug(M, ...) \
	syslog(LOG_DEBUG, "[DEBUG] %s:%d:%s: " M "\n", \
	       __FILE__, __LINE__, __func__, ##__VA_ARGS__)


#define err_if(A, M, ...) \
	if((A)) {log_error(M, ##__VA_ARGS__); goto free_exit;}

extern uint64_t virtnet_test_type;
extern char *feature_names[];
extern int status_id[];
extern char *status_names[];
struct virtnet_init_attr;
struct virtnet_context *ctx;
struct virtnet_device;

typedef void (*virtnet_epoll_callback)(void *ctx);
struct virtnet_epoll_data {
	void *ctx;
	virtnet_epoll_callback cb;
};

static int inline virtnet_ether_addr_is_zero(uint8_t *mac)
{
	const uint16_t *w = (const uint16_t *)mac;

	return (w[0] | w[1] | w[2]) == 0;
}


static int inline virtnet_test_condition(uint32_t condition)
{
	return (virtnet_test_type & condition) ? 1 : 0;
}

static inline unsigned int log2above(unsigned int v)
{
	unsigned int l;
	unsigned int r;

	for (l = 0, r = 0; (v >> 1); ++l, v >>= 1)
		r |= (v & 1);
	return l + r;
}

struct ibv_device *virtnet_get_ib_dev(const char *ib_name);
int virtnet_ibdev_to_pci_addr(const struct ibv_device *ibdev,
			      char *pci_addr_str);
int virtnet_get_pci_function(const char *str, uint8_t *num);
struct virtnet_port_ctx *
virtnet_ibdev_to_ibport(const struct virtnet_context *ctx,
			const struct ibv_device *ibdev);

void virtnet_log_open(void);
void virtnet_log_close(void);
void virtnet_set_log_level(int level);

int virtnet_get_mtu(struct ibv_context *ctx, uint16_t *mtu);
int virtnet_set_mtu(const char *dev, uint16_t mtu);

int virtnet_epoll_close(void);
int virtnet_epoll_init(int n);
int virtnet_epoll_del(int fd);
int virtnet_epoll_add(int fd, struct virtnet_epoll_data *data);
int virtnet_epoll_wait(int timeout_ms);

void virtnet_get_bdf(const struct virtnet_device *dev, char *str, uint16_t len);
void virtnet_get_mac(const struct virtnet_device *dev, char *str, uint16_t len);

#ifdef __cplusplus
}
#endif

#endif
